package com.ruoyi.web.controller.monitor;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.DeviceInfo;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.SaTokenUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 在线用户监控
 *
 * @author ruoyi
 */
@Tag(name = "在线用户监控")
@RestController
@RequestMapping("/monitor/online")
@RequiredArgsConstructor
public class SysUserOnlineController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(SysUserOnlineController.class);

    private final ISysUserOnlineService userOnlineService;
    private final SaTokenDao saTokenDao;
    private final SysLoginService loginService;

    @Value("${sa-token.token-name}")
    private String tokenName;

    @Operation(summary = "在线用户列表")
    @SaCheckPermission("monitor:online:list")
    @GetMapping("/list")
    public TableDataInfo list(String ipaddr, String userName) {
        List<SysUserOnline> userOnlineList = new ArrayList<>();
        try {
            // 构建正确的key模式：tokenName:login:session:*
            String keyPattern = tokenName + ":login:session:*";
            List<String> sessionKeys = saTokenDao.searchData(keyPattern, "*", 0, -1, false);
            for (String sessionKey : sessionKeys) {
                try {
                    // 从key中提取loginId
                    String prefix = tokenName + ":login:session:";
                    String loginId = sessionKey.replace(prefix, "");
                    // 检查该用户是否仍在线
                    if (StpUtil.isLogin(loginId)) {
                        // 直接从Redis获取session数据
                        String sessionData = saTokenDao.get(sessionKey);
                        if (StringUtils.isNotEmpty(sessionData)) {
                            JSONObject sessionJson = JSON.parseObject(sessionData);

                            // 从dataMap中获取login_user_key
                            JSONObject dataMap = sessionJson.getJSONObject("dataMap");
                            if (dataMap != null) {
                                // 获取terminalList，为每个终端创建在线用户记录
                                List<JSONObject> terminalList = null;
                                JSONArray terminalArray = sessionJson.getJSONArray("terminalList");
                                if (terminalArray != null) {
                                    terminalList = terminalArray.toJavaList(JSONObject.class);
                                }

                                // 获取基础用户信息
                                LoginUser baseLoginUser = parseLoginUserFromDataMap(dataMap);

                                if (baseLoginUser != null && terminalList != null && !terminalList.isEmpty()) {
                                    for (JSONObject terminal : terminalList) {
                                        String tokenValue = terminal.getString("tokenValue");

                                        // 获取设备信息并创建完整的LoginUser对象
                                        LoginUser loginUserWithDevice = createLoginUserWithDevice(baseLoginUser, dataMap, tokenValue);

                                        if (loginUserWithDevice != null) {
                                            // 根据筛选条件过滤并添加到列表
                                            SysUserOnline online = filterAndCreateOnlineUser(ipaddr, userName, loginUserWithDevice);

                                            if (online != null) {
                                                online.setTokenId(tokenValue);
                                                userOnlineList.add(online);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    log.warn("处理session异常: {}", e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("获取在线用户列表异常", e);
        }
        // 按登录时间降序排列
        userOnlineList = userOnlineList.stream().filter(online -> online.getLoginTime() != null).sorted((a, b) -> b.getLoginTime().compareTo(a.getLoginTime())).collect(Collectors.toList());
        return getDataTable(userOnlineList);
    }


    /**
     * 强退用户
     */
    @Operation(summary = "强退用户")
    @SaCheckPermission("monitor:online:forceLogout")
    @Log(title = "在线用户", businessType = BusinessType.FORCE)
    @DeleteMapping("/{tokenId}")
    public AjaxResult forceLogout(@PathVariable String tokenId) {
        try {
            // 先清理设备信息，然后再强退用户
            loginService.cleanupTokenDeviceInfo(tokenId);
            // 使用SA-Token的强退API
            StpUtil.logoutByTokenValue(tokenId);
            return success("强退成功");
        } catch (Exception e) {
            log.error("强退用户失败", e);
            return error("强退失败，请检查日志");
        }
    }

    /**
     * 获取我的登录设备列表
     */
    @Operation(summary = "获取我的登录设备列表")
    @GetMapping("/myDevices")
    public AjaxResult getMyDevices() {
        try {
            // 获取当前登录用户ID
            Long currentUserId = SaTokenUtils.getLoginUser().getUserId();
            
            List<SysUserOnline> myDevices = new ArrayList<>();
            
            // 构建正确的key模式：tokenName:login:session:currentUserId
            String sessionKey = tokenName + ":login:session:" + currentUserId;
            String sessionData = saTokenDao.get(sessionKey);
            
            if (StringUtils.isNotEmpty(sessionData)) {
                JSONObject sessionJson = JSON.parseObject(sessionData);
                JSONObject dataMap = sessionJson.getJSONObject("dataMap");
                
                if (dataMap != null) {
                    // 获取terminalList，为每个终端创建设备记录
                    JSONArray terminalArray = sessionJson.getJSONArray("terminalList");
                    if (terminalArray != null) {
                        List<JSONObject> terminalList = terminalArray.toJavaList(JSONObject.class);
                        
                        // 获取基础用户信息
                        LoginUser baseLoginUser = parseLoginUserFromDataMap(dataMap);
                        
                        if (baseLoginUser != null) {
                            for (JSONObject terminal : terminalList) {
                                String tokenValue = terminal.getString("tokenValue");
                                
                                // 获取设备信息并创建完整的LoginUser对象
                                LoginUser loginUserWithDevice = createLoginUserWithDevice(baseLoginUser, dataMap, tokenValue);
                                
                                if (loginUserWithDevice != null) {
                                    // 创建设备信息对象（不进行任何筛选）
                                    SysUserOnline device = userOnlineService.loginUserToUserOnline(loginUserWithDevice);
                                    if (device != null) {
                                        device.setTokenId(tokenValue);
                                        // 标记当前设备
                                        String currentToken = StpUtil.getTokenValue();
                                        if (tokenValue.equals(currentToken)) {
                                            device.setDeptName("当前设备"); // 临时使用deptName字段标记
                                        }
                                        myDevices.add(device);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            // 按登录时间降序排列
            myDevices = myDevices.stream()
                .filter(device -> device.getLoginTime() != null)
                .sorted((a, b) -> b.getLoginTime().compareTo(a.getLoginTime()))
                .collect(Collectors.toList());
            
            return success(myDevices);
            
        } catch (Exception e) {
            log.error("获取我的登录设备列表异常", e);
            return error("获取失败：" + e.getMessage());
        }
    }

    /**
     * 强退我的指定设备
     */
    @Operation(summary = "强退我的指定设备")
    @Log(title = "强退设备", businessType = BusinessType.FORCE)
    @DeleteMapping("/myDevices/{tokenId}")
    public AjaxResult forceLogoutMyDevice(@PathVariable String tokenId) {
        try {
            // 获取当前登录用户ID
            Long currentUserId = SaTokenUtils.getLoginUser().getUserId();
            
            // 验证token是否属于当前用户
            Object tokenUserId = StpUtil.getLoginIdByToken(tokenId);
            if (tokenUserId == null || !currentUserId.equals(Long.valueOf(tokenUserId.toString()))) {
                return error("无权限强退该设备");
            }
            
            // 不允许强退当前设备
            String currentToken = StpUtil.getTokenValue();
            if (tokenId.equals(currentToken)) {
                return error("不能强退当前设备");
            }
            
            // 先清理设备信息，然后再强退
            loginService.cleanupTokenDeviceInfo(tokenId);
            // 使用SA-Token的强退API
            StpUtil.logoutByTokenValue(tokenId);
            
            return success("设备强退成功");
        } catch (Exception e) {
            log.error("强退我的设备失败", e);
            return error("强退失败：" + e.getMessage());
        }
    }

    /**
     * 从dataMap中解析LoginUser对象
     *
     * @param dataMap session数据映射
     * @return LoginUser对象
     */
    private LoginUser parseLoginUserFromDataMap(JSONObject dataMap) {
        Object loginUserObj = dataMap.get(Constants.LOGIN_USER_KEY);
        if (loginUserObj == null) {
            return null;
        }
        try {
            if (loginUserObj instanceof LoginUser) {
                return (LoginUser) loginUserObj;
            } else if (loginUserObj instanceof JSONObject) {
                return JSON.toJavaObject((JSONObject) loginUserObj, LoginUser.class);
            } else {
                String jsonStr = JSON.toJSONString(loginUserObj);
                return JSON.parseObject(jsonStr, LoginUser.class);
            }
        } catch (Exception e) {
            log.warn("解析LoginUser对象异常: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 创建包含设备信息的LoginUser对象
     *
     * @param baseLoginUser 基础用户信息
     * @param dataMap       session数据映射
     * @param tokenValue    token值
     * @return 包含设备信息的LoginUser对象
     */
    private LoginUser createLoginUserWithDevice(LoginUser baseLoginUser, JSONObject dataMap, String tokenValue) {
        // 获取设备信息
        String deviceInfoKey = Constants.LOGIN_USER_KEY + ":device:" + tokenValue;
        Object deviceInfoObj = dataMap.get(deviceInfoKey);
        if (deviceInfoObj == null) {
            log.debug("未找到token对应的设备信息: {}", tokenValue);
            return null;
        }
        try {
            DeviceInfo deviceInfo = null;
            if (deviceInfoObj instanceof DeviceInfo) {
                deviceInfo = (DeviceInfo) deviceInfoObj;
            } else if (deviceInfoObj instanceof JSONObject) {
                deviceInfo = JSON.toJavaObject((JSONObject) deviceInfoObj, DeviceInfo.class);
            } else {
                String jsonStr = JSON.toJSONString(deviceInfoObj);
                deviceInfo = JSON.parseObject(jsonStr, DeviceInfo.class);
            }

            if (deviceInfo != null) {
                // 创建新的LoginUser对象，包含基础信息和设备信息
                LoginUser loginUserWithDevice = new LoginUser();
                loginUserWithDevice.setUserId(baseLoginUser.getUserId());
                loginUserWithDevice.setDeptId(baseLoginUser.getDeptId());
                loginUserWithDevice.setUser(baseLoginUser.getUser());
                loginUserWithDevice.setPermissions(baseLoginUser.getPermissions());
                // 应用设备信息
                DeviceInfo.applyToLoginUser(loginUserWithDevice, deviceInfo);
                return loginUserWithDevice;
            }
        } catch (Exception e) {
            log.warn("创建LoginUser对象异常: token={}, error={}", tokenValue, e.getMessage());
        }

        return null;
    }

    /**
     * 根据筛选条件过滤并创建在线用户对象
     *
     * @param ipaddr    IP地址筛选条件
     * @param userName  用户名筛选条件
     * @param loginUser 登录用户信息
     * @return 在线用户对象
     */
    private SysUserOnline filterAndCreateOnlineUser(String ipaddr, String userName, LoginUser loginUser) {
        try {
            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
                return userOnlineService.selectOnlineByInfo(ipaddr, userName, loginUser);
            } else if (StringUtils.isNotEmpty(ipaddr)) {
                return userOnlineService.selectOnlineByIpaddr(ipaddr, loginUser);
            } else if (StringUtils.isNotEmpty(userName)) {
                return userOnlineService.selectOnlineByUserName(userName, loginUser);
            } else {
                return userOnlineService.loginUserToUserOnline(loginUser);
            }
        } catch (Exception e) {
            log.warn("创建在线用户对象异常: user={}, error={}", loginUser.getUsername(), e.getMessage());
            return null;
        }
    }
}
